<?php


namespace services\patient;

use api\modules\mini\forms\patient\PatientForm;
use common\components\Service;
use common\helpers\CommonHelper;
use common\helpers\DateHelper;
use common\helpers\RedisHelper;
use common\models\linkedcare\LinkedcarePatientsFeeDetail;
use common\models\patient\Patient;
use common\models\patient\PatientOrder;
use common\models\patient\PatientType;
use common\models\spoker\Spoker;
use common\models\wechat\WechatTemplate;
use services\base\TreatmentProjectService;
use services\linkedcare\LinkedcarePatientsFeeDetailService;
use services\linkedcare\LinkedcarePatientsRecordService;
use services\log\LogMemberOpService;
use services\common\MessageService;
use services\linkedcare\LinkedcareAuthService;
use services\shop\ShopService;
use services\spoker\SpokerService;
use services\wechat\WechatTemplateService;
use Yii;
use yii\db\Exception;
use yii\db\Query;

class PatientService extends Service
{
    /**
     * 患者列表
     * @param array $params
     * @param int $page
     * @param int $pageSize
     * @return array
     */
    public function getListPage(array $params = [], int $page = 0, int $pageSize = 0): array
    {
        $query = Patient::find()
            ->select([
                'id', 'name', 'mobile', 'type_id', 'last_order_id', 'remark', 'created_at',
            ])
            ->where([
                'user_id' => Yii::$app->user->identity->member_id,
                'status' => Patient::STATUS_ENABLE
            ]);
        if (isset($params['name']) && $params['name']) {
            $query->andWhere(['like', 'name', $params['name']]);
        }
        // 排序
        $order = [
            'created_at' => SORT_DESC,
            'id' => SORT_DESC,
        ];

        return $this->getListPageRes($query, $page, $pageSize, $order);
    }

    /**
     * 格式化列表
     * @param array $data
     * @return array
     */
    public function getListPageFormat(array $data = []): array
    {
        // 最近一次预约记录
        $patientOrderMap = PatientOrderService::getPatientOrderMap(array_column($data, 'last_order_id'));
        $list = [];
        foreach ($data as $row) {
            $treatmentProjectId = $patientOrderMap[$row['id']]->treatment_project_id ?? 0;
            $treatmentProject = RedisHelper::getTreatmentProject($treatmentProjectId);
            $patientType = RedisHelper::getPatientType($row['type_id']);
            $list[] = [
                'id' => $row['id'],
                'name' => $row['name'],
                'treatment_project' => $treatmentProject ? $treatmentProject->name : '',
                'type_str' => $patientType && $patientType->status != PatientType::STATUS_DELETE ? $patientType->name : '',
                'remark' => $row['remark'],
                'created_at_str' => DateHelper::timestampToDate($row['created_at'], DateHelper::DATE_FORMAT_1),
            ];
        }
        return $list;
    }

    /**
     * 患者信息
     * @param int $id
     * @return array
     */
    public function getInfo(int $id): array
    {
        $patient = Patient::findOne($id);
        if (empty($patient)) {
            return CommonHelper::api(422, '患者不存在');
        }
        $patientOrder = $patient->last_order_id ? PatientOrder::findOne($patient->last_order_id) : null;

        // 消费详情
        $record = $this->getPatientConsumeDetail($id);
        $data = [
            'id' => $patient->id,
            'name' => $patient->name,
            'mobile' => $patient->mobile,
            'type_str' => RedisHelper::getPatientType($patient->type_id)->name ?? '',
            'discount_rate' => $patient->discount_rate,
            'remark' => $patient->remark,
            'order_id' => $patient->last_order_id,
            'arrive_num' => LinkedcarePatientsRecordService::getUserArriveNum(['patient_id' => $id]),// 到店数
            'consume_money' => LinkedcarePatientsFeeDetailService::getUserConsumeMoney(['patient_id' => $id]),// 销售额
            'created_at_str' => DateHelper::timestampToDate($patient->created_at, DateHelper::DATE_FORMAT_4),
            'record_list' => $this->getPatientConsumeDetailFormat($record['list']),
        ];
        if (empty($patientOrder) || PatientOrderService::isExpired($patientOrder->order_time)) {
            $data['order_id'] = 0;
        }
        return $data;
    }

    /**
     * 患者消费详情记录【消费+预约】
     * @param int $patientId
     * @param int $page
     * @param int $pageSize
     * @return array
     */
    public function getPatientConsumeDetail(int $patientId, int $page = 0, int $pageSize = 0): array
    {
        $query1 = LinkedcarePatientsFeeDetailService::getPatientConsumeDetailQuery($patientId);
        $query2 = PatientOrderService::getPatientOrderDetailQuery($patientId);
        $query = $query1->union($query2);
        $unionQuery = (new Query())->from(['u' => $query]);
        $order = [
            'sort_time' => SORT_DESC,
            'id' => SORT_DESC,
        ];
        $data = $this->getListPageRes($unionQuery, $page, $pageSize, $order);
        return $data;
    }

    /**
     * 格式化
     * @param array $data
     * @return array
     */
    public function getPatientConsumeDetailFormat(array $data): array
    {
        // 格式化
        $orderIds = $consumeIds = [];
        foreach ($data as $row) {
            if ($row['type'] == 1) {
                $consumeIds[] = $row['id'];
            } else {
                $orderIds[] = $row['id'];
            }
        }
        $consumeMap = LinkedcarePatientsFeeDetailService::getPatientDetailConsumeMap($consumeIds);
        $orderMap = PatientOrderService::getPatientDetailOrderMap($orderIds);
        $dataFormat = [];

        foreach ($data as $row) {
            $item = $row['type'] == 1 ? $consumeMap[$row['id']] ?? [] : $orderMap[$row['id']] ?? [];
            $dataFormat[] = array_merge([
                'id' => $row['id'],
                'type' => $row['type'],
            ], $item);
        }
        return $dataFormat;
    }

    /**
     * 患者编辑信息
     * @param int $id
     * @return array
     */
    public function getEditInfo(int $id): array
    {
        $data = [];
        $patient = Patient::findOne($id);
        if ($patient) {
            $data = [
                'id' => $patient->id,
                'name' => $patient->name,
                'mobile' => $patient->mobile,
                'type_id' => $patient->type_id,
                'type_name' => RedisHelper::getPatientType($patient->type_id)->name ?? '',
                'discount_rate' => $patient->discount_rate,
                'remark' => $patient->remark,
            ];
        } else {
            // 订阅模板IDs
            $data['template_ids'] = WechatTemplateService::getPatientSubTempalteIds();
        }
        $condition = [
            'treatment_project' => TreatmentProjectService::getEditCondition(),
            'shop' => ShopService::getEditCondition(),
            'patient_type' => PatientTypeService::getEditCondition(),
        ];
        if ($patient) {
            if (in_array($data['type_id'], array_column($condition['patient_type'], 'value')) === false) {
                $data['type_id'] = 0;
            }
        }
        return array_merge($data, ['condition' => $condition]);
    }

    /**
     * 格式化折扣
     * @param int $discountRate
     * @return string
     */
    public static function getDiscountRate(int $discountRate): string
    {
        $discountRateFormat = number_format($discountRate/10, 1, '.', '');
        return $discountRate == 100 ? '无折扣' : "{$discountRateFormat}折";
    }

    /**
     * 获取下一位患者病历编号
     * @param Patient $patient 患者
     * @return string
     */
    public static function getDatePatientNum(Patient $patient): string
    {
        $privateDate = date('Ymd', $patient->created_at);
        $startTime = strtotime($privateDate);
        $endTime = $startTime + 86400;
        $lastPatient = Patient::find()
            ->where([
                'eky_repeat' => Patient::EKY_REPEAT_NO
            ])
            ->andWhere(['>=', 'created_at', $startTime])
            ->andWhere(['<', 'created_at', $endTime])
            ->orderBy(['private_id' => SORT_DESC])
            ->one();
        $num = $lastPatient && $lastPatient->private_id ? substr($lastPatient->private_id, 12) : 0;
        return self::getUniquePrivateId($privateDate, (int)$num);
    }

    /**
     * 获取病历号
     * @param string $privateDate
     * @param int $no
     * @return string
     */
    public static function getUniquePrivateId(string $privateDate, int $no): string
    {
        $num = $no + 1;
        $numStr = str_pad($num, 4, '0', STR_PAD_LEFT);
        $privateId = "JGKQ{$privateDate}".$numStr;
        if (Patient::findOne(['private_id' => $privateId])) {
            return self::getUniquePrivateId($privateDate, $num + 1);
        }
        return $privateId;
    }

    /**
     * 手机号是否存在重复
     * @param string $mobile
     * @return bool
     */
    public static function hasRepeatMobie(string $mobile): bool
    {
        $exist = Patient::findOne([
            'mobile' => $mobile,
            'eky_repeat' => Patient::EKY_REPEAT_YES,
        ]);
        return $exist ? true : false;
    }

    /**
     * 统计用户登记患者数
     * @return int
     */
    public static function getUserPatientNum(array $params = []): int
    {
        $query = Patient::find()
            ->where([
                'user_id' => Yii::$app->user->identity->member_id,
                'status' => Patient::STATUS_ENABLE
            ]);
        if (isset($params['start_time']) && $params['start_time']) {
            $query->andWhere(['>=', 'created_at', strtotime($params['start_time'])]);
        }
        if (isset($params['end_time']) && $params['end_time']) {
            $query->andWhere(['<=', 'created_at', strtotime($params['end_time'].' 23:59:59')]);
        }
        $total = $query->count();
        return $total ?: 0;
    }

    /**
     * 获取患者新增月账单统计
     * @return array
     */
    public static function getMonthBillByUser(array $params): array
    {
        $query = Patient::find()->alias('p')
            ->select(['p.user_id', 'COUNT(*) add_num'])
            ->where([
                'p.status' => Patient::STATUS_ENABLE
            ])
            ->groupBy('p.user_id');
        if (isset($params['start_time']) && $params['start_time']) {
            $query->andWhere(['>=', 'p.created_at', strtotime($params['start_time'])]);
        }
        if (isset($params['end_time']) && $params['end_time']) {
            $query->andWhere(['<=', 'p.created_at', strtotime($params['end_time'].' 23:59:59')]);
        }
        return $query->having(['>', 'add_num', 0])->indexBy('user_id')->asArray()->all();
    }

    /**
     * 统计新增患者数排行榜
     * @return array
     */
    public static function getRankList(array $params = []): array
    {
        $query = Patient::find()->alias('p')
            ->select(['p.spoker_id', 'COUNT(*) add_num', 'MAX(p.created_at) created_at'])
            ->where([
                'p.status' => Patient::STATUS_ENABLE
            ])
            ->groupBy('p.spoker_id');
        if (isset($params['start_time']) && $params['start_time']) {
            $query->andWhere(['>=', 'p.created_at', strtotime($params['start_time'])]);
        }
        if (isset($params['end_time']) && $params['end_time']) {
            $query->andWhere(['<=', 'p.created_at', strtotime($params['end_time'].' 23:59:59')]);
        }
        $list = $query
            ->orderBy([
                'add_num' => SORT_DESC,
                'created_at' => SORT_ASC,
            ])
            ->limit(10)
            ->asArray()
            ->all();
        // 代言人集合
        $spokerMap = SpokerService::getMapByIds(array_column($list, 'spoker_id'));
        $listFormat = [];
        foreach ($list as $row) {
            $listFormat[] = [
                'spoker_id' => $row['spoker_id'],
                'add_num' => $row['add_num'],
                'name' => CommonHelper::getRankNameStr($spokerMap[$row['spoker_id']]->name ?? ''),
            ];
        }
        return $listFormat;
    }

    public static function editModel(Patient $patient, PatientForm $model): Patient
    {
        $patient->user_id = Yii::$app->user->identity->member_id;
        $patient->spoker_id = Yii::$app->user->identity->user->spoker_id;
        $patient->name = $model->name;
        $patient->mobile = $model->mobile;
        $patient->type_id = $model->type_id ?: 0;
        $patient->discount_rate = $model->discount_rate;
        $patient->eky_repeat = Patient::EKY_REPEAT_NO;
        $patient->eky_confirm = Patient::EKY_CONFIRM_NO;
        $patient->data_status = Patient::DATA_STATUS_ADD;
        $patient->status = Patient::STATUS_ENABLE;
        $patient->remark = $model->remark;
        $patient->saveAndCheckResult();

        return $patient;
    }

    /**
     * 新增客户
     * @param PatientForm $model
     * @return void
     * @throws \Exception
     */
    public static function create(PatientForm $model): void
    {
        try {
            // 查询E看牙是否重复
            $ekyRepeat = LinkedcareAuthService::checkPhoneRepeatByEky($model->mobile, 0);
//            print_r($ekyRepeat);exit();
            $transaction = Yii::$app->db->beginTransaction();
            // 1.新增患者
            $patient = new Patient();
            $patient = self::editModel($patient, $model);
            $patient->eky_repeat = $ekyRepeat['is_repeat'] ? Patient::EKY_REPEAT_YES : Patient::EKY_REPEAT_NO;
            if ($ekyRepeat['is_repeat'] === false) {
                $patient->private_id = self::getDatePatientNum($patient);
            }
            $patient->saveAndCheckResult();
            // 2.添加预约记录
            if ($model->order_time) {
                $patientOrder = PatientOrderService::createOrder($patient, $model);
                // 更新最后一次预约ID
                $patient->last_order_id = $patientOrder->id;
                $patient->data_status = Patient::DATA_STATUS_ORDER;
                $patient->saveAndCheckResult();
            }
            $transaction->commit();
            // 3.E看牙同步
            if ($ekyRepeat['is_repeat'] === false) {
                $date = date('Ymd', $patient->created_at);
                $logReqPatient = LinkedcareAuthService::addPatient($date, $patient);
                // 未新增成功，抛出异常
                /*$resData = $logReqPatient->add_user_resp ?? [];
                if (!isset($resData['id']) || empty($resData['id'])) {
                    throw new \Exception(json_encode($resData));
                }*/
            }
            // 4.发送通知
            $cnt = self::getUserPatientNum();
            $sendData = [
                'thing1' => $patient->name,
                'thing4' => $patient->eky_repeat == Patient::EKY_REPEAT_NO ? "录单成功，这是您录的第{$cnt}个客户！" : '客户信息重复，正在审核',
            ];
            WechatTemplateService::sendTemplate(WechatTemplate::TYPE_ADD_PATIENT, $sendData, Yii::$app->user->identity->user->wx_mini_openid);

            if ($patient->eky_repeat == Patient::EKY_REPEAT_YES) {// 重复推送钉钉提醒
                MessageService::sendDingtalk(Yii::$app->params['dingtalk_code_patient_repeate'], [
                    'patient_name' => $patient->name,
                    'patient_mobile' => $patient->mobile,
                    'spoker_name' => Yii::$app->user->identity->user->spoker->name,
                ]);
            }
        } catch (\Exception $e) {
            $transaction->rollBack();
            throw $e;
        }
    }

    /**
     * 编辑客户
     * @param PatientForm $model
     * @return void
     * @throws \Exception
     */
    public static function update(PatientForm $model): void
    {
        try {
//            $transaction = Yii::$app->db->beginTransaction();

            // 1.编辑患者
            self::editModel($model->getPatient(), $model);

//            $transaction->commit();
        } catch (\Exception $e) {
//            $transaction->rollBack();
            throw $e;
        }
    }

    /**
     * pc客户列表
     *
     * @param array $params
     * @param int $page
     * @param int $pageSize
     * @return array
     */
    public function patientList(array $params = [], int $page = 0, int $pageSize = 0): array
    {
        $query = Patient::find()
            ->alias('p')
            ->select(['p.*', 's.name spoker_name'])
            ->innerJoin(Spoker::tableName() . ' s', 'p.spoker_id = s.id')
            ->where([
                'p.status' => Patient::STATUS_ENABLE
            ]);
        // 搜索患者手机号、姓名
        if (isset($params['keywords']) && $params['keywords']) {
            if (is_numeric($params['keywords'])) {
                $query->andWhere(['like', 'p.mobile', "{$params['keywords']}%", false]);
            } else {
                $query->andWhere(['like', 'p.name', $params['keywords']]);
            }
        }
        // 状态
        if (isset($params['data_status']) && is_numeric($params['data_status'])) {
            $query->andWhere(['p.data_status' => $params['data_status']]);
        }
        if (isset($params['spoker_id']) && $params['spoker_id']) {
            $query->andWhere(['p.spoker_id' => $params['spoker_id']]);
        }
        if (isset($params['eky_repeat']) && $params['eky_repeat'] == Patient::EKY_REPEAT_YES) {
            $query->andWhere(['p.eky_repeat' => $params['eky_repeat']]);
        }
        // 排序
        $order = [
            'p.created_at' => SORT_DESC,
            'p.id' => SORT_DESC,
        ];

        $list = $this->getListPageRes($query, $page, $pageSize, $order);
        foreach ($list['list'] as &$val) {
            $patientType = RedisHelper::getPatientType($val['type_id']);
            $val['type_str'] = $patientType && $patientType->status != PatientType::STATUS_DELETE ? $patientType->name : '';
            $val['data_status_str'] = $val['data_status'] ? Patient::$dataStatuss[$val['data_status']] : '';
            $val['created_at_str'] = DateHelper::timestampToDate($val['created_at'], DateHelper::DATE_FORMAT_1);

            $val['arrive_num'] = LinkedcarePatientsRecordService::getUserArriveNum(['patient_id' => $val['id']]);// 到店数
            $val['sale_money'] = LinkedcarePatientsFeeDetailService::getUserConsumeMoney(['patient_id' => $val['id']]);// 销售额
        }
        return $list;
    }

    /**
     *  下拉筛选状态
     * @return array[]
     */
    public static function getDataStatusCondition(): array
    {
        return [
            ['value' => Patient::DATA_STATUS_ADD, 'label' => Patient::$dataStatuss[Patient::DATA_STATUS_ADD]],
            ['value' => Patient::DATA_STATUS_ORDER, 'label' => Patient::$dataStatuss[Patient::DATA_STATUS_ORDER]],
            ['value' => Patient::DATA_STATUS_IN_HOSPITAL, 'label' => Patient::$dataStatuss[Patient::DATA_STATUS_IN_HOSPITAL]],
        ];
    }

    /**
     * 重复客户关联
     * @param $params
     * @return array
     * @throws Exception
     * @throws \Exception
     */
    public function connect($params): array
    {
        $patientIs = Patient::findOne($params['id']);
        if (!$patientIs) {
            return CommonHelper::api(422, '客户不存在');
        }
        if ($patientIs->eky_repeat == Patient::EKY_REPEAT_NO) {
            return CommonHelper::api(422, '客户不重复');
        }
        if ($patientIs->eky_confirm == Patient::EKY_REPEAT_YES) {
            return CommonHelper::api(422, '客户已关联');
        }
        Patient::updateAll([
            'eky_confirm' => Patient::EKY_REPEAT_YES,
            'updated_at' => time()
        ], ['id' => $params['id']]);

        // 添加日志
        LogMemberOpService::createPatientLog('connect', Patient::findOne($params['id']));
        return ['id' => $params['id']];
    }

    /**
     * 删除重复客户
     * @param $params
     * @return array
     * @throws Exception
     */
    public function delete($params): array
    {
        $patientIs = Patient::findOne($params['id']);
        if (!$patientIs) {
            return CommonHelper::api(422, '客户不存在');
        }
        if ($patientIs->eky_repeat == Patient::EKY_REPEAT_NO) {
            return CommonHelper::api(422, '客户不重复，不能删除');
        }
        if ($patientIs->eky_confirm == Patient::EKY_REPEAT_YES) {
            return CommonHelper::api(422, '客户已关联，不能删除');
        }
        $transaction = Yii::$app->db->beginTransaction();
        try {
            Patient::updateAll([
                'status' => Patient::STATUS_DELETE,
                'updated_at' => time()
            ], ['id' => $params['id']]);

            // 添加日志
            LogMemberOpService::createPatientLog('delete', Patient::findOne($params['id']));
            $transaction->commit();
            return ['id' => $params['id']];
        } catch (\Exception $e) {
            $transaction->rollBack();
            throw $e;
        }
    }
}
